package com.le.bbs.controller;

import com.le.bbs.config.JwtTokenUtil;
import com.le.bbs.model.JwtResponse;
import com.le.bbs.service.JwtUserDetailsService;
import com.le.bean.User;
import com.sun.org.apache.xalan.internal.xsltc.dom.SimpleResultTreeImpl;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("user") // http://localhost:port/user
@Slf4j
@Api(tags = "用户登录鉴权管理")
public class JwtAuthenticationController {
    @Autowired
    private AuthenticationManager authenticationManager; // spring security提供的验证管理器
    @Autowired
    private JwtTokenUtil jwtTokenUtil; // 生成和验证 token 的工具类
    @Autowired
    private JwtUserDetailsService jwtUserDetailsService; // 访问 数据库，验证用户名和密码的 业务层

    @RequestMapping({"/signout"}) // 退出操作
    public Map<String, Object> signout(@RequestHeader("Authorization") String bearerToken, HttpSession session) {
        Map<String, Object> result = new HashMap<>();
        session.removeAttribute("user");
        session.invalidate();
        result.put("code", 1);
        return result;
    }

    @RequestMapping({"/loginCheck"})
    public Map<String, Object> loginCheck(@RequestHeader("Authorization") String bearerToken, HttpSession session){
        Map<String, Object> result = new HashMap<>();

        if (session.getAttribute("user") == null){
            result.put("code", 0);
            result.put("msg", "用户暂未登录！");
            return result;
        }

        // 再 token认证
        System.out.println("接收到的token为：" + bearerToken);
        String token = bearerToken.substring(7);

        User user = (User) session.getAttribute("user");
        UserDetails ud = jwtUserDetailsService.loadUserByUsername(user.getUname());
        Boolean b = jwtTokenUtil.validateToken(token, ud); // 效验用户名与有效时间

        if (b){
            result.put("code", 1);
            result.put("data", user);
        } else {
            // 无效
            result.put("code", 0);
        }
        return result;
    }

    @RequestMapping({"/getTokenDetail"})
    public Claims firstPage(@RequestHeader("Authorization") String bearerToken){
        System.out.println("接收到的token为：" + bearerToken);
        String token = bearerToken.substring(7); // 因为 bearerToken的开头是 "Bearer "，不是token的一部分，要去除！
        Claims claims = jwtTokenUtil.getAllClaimsFromToken(token);
        return claims;
    }

    @ApiOperation(value = "用户登录操作")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "uname", value = "用户名", required = true),
            @ApiImplicitParam(name = "upass", value = "密码", required = true),
            @ApiImplicitParam(name = "valcode", value = "验证码", required = true)
    })
    @RequestMapping(value = "/login.action", method = RequestMethod.POST)
    public Map<String, Object> createAuthenticationToken(String uname, String upass, String valcode, HttpSession session) throws Exception {
        // 加入验证码校验
        Map<String, Object> result = new HashMap<>();
        String code = (String) session.getAttribute("code");
        if (!code.equals(valcode)){
            result.put("code", 0);
            result.put("msg", "验证码错误");
            return result;
        }

        authenticate(uname, upass);
        // 根据用户名取出详情
        final UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(uname);
        // 根据用户详情生成token
        String token = jwtTokenUtil.generateToken(userDetails);

        // 在业务层多增加一个方法，根据 uname再查一次User(com.le.bean)
        User user = jwtUserDetailsService.findUserByUname(uname);
        session.setAttribute("user", user); // 存到 session中

        // 将 token 回传给客户端
        result.put("code", 1);
        result.put("data", token);
        return result;
    }


    @ApiOperation(value = "用户注册操作")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "uname", value = "用户名", required = true),
            @ApiImplicitParam(name = "upass", value = "密码", required = true),
            @ApiImplicitParam(name = "valcode", value = "验证码", required = true),
            @ApiImplicitParam(name = "head", value = "头像", required = true),
            @ApiImplicitParam(name = "gender", value = "性别", required = true)
    })
    @RequestMapping(value = "/reg.action", method = RequestMethod.POST)
    public Map<String, Object> reg(User user, HttpSession session) throws Exception {
        Map<String, Object> result = new HashMap<>();
        // 注册时间
        Date date = new Date();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        user.setRegtime(df.format(date));

        BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();
        user.setUpass( bcpe.encode( user.getUpass()));

        User u = this.jwtUserDetailsService.reg(user);
        result.put("code", 1);
        result.put("data", u);
        return result;
    }



    private void authenticate(String username, String password) throws Exception {
        try {
            // 调用认证管理器，对输入的用户名和密码进行认证，验证用户名和密码是否正确
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e){
            throw new Exception("USER_DISABLED", e);
        }catch (BadCredentialsException e){
            throw new Exception("INVALID_CREDENTIALS", e);
        }
    }
}
